﻿using System;
using System.Diagnostics;
using System.IO;
using System.Collections;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections.Concurrent;
using System.Runtime.Serialization;
using System.Xaml;

using alib;
using alib.String;
using alib.String.Builder;
using alib.Debugging;
using alib.Enumerable;
using alib.Character;
using alib.Collections;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public struct FsPathPair
	{
		public FsPathPair(FsPath path1, FsPath path2)
		{
			this.path1 = path1;
			this.path2 = path2;
		}
		public FsPathPair(Isometrics im, String s1, String s2)
		{
			this.path1 = new FsPath(im, s1);
			this.path2 = new FsPath(im, s2);
		}
		public FsPath path1;
		public FsPath path2;
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class BootstrapFsPathList : ItemsList<BootstrapFsPath>, IList
	{
		public BootstrapFsPathList(IIdentity prv, IEnumerable<BootstrapFsPath> items)
			: base(prv, items)
		{
		}
		public BootstrapFsPathList(IIdentity prv)
			: this(prv, default(IEnumerable<BootstrapFsPath>))
		{
		}
		public BootstrapFsPathList()
			: this(SysObj.Instance)
		{
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class BfsTypeConverter : TypeConverter
	{
		public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
		{
			return sourceType == typeof(String);
		}
		public override bool CanConvertTo(ITypeDescriptorContext context, System.Type destinationType)
		{
			return destinationType == typeof(String);
		}
		public override Object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, Object value)
		{
			return new BootstrapFsPath(context as IIdentity ?? SysObj.Instance, (String)value);
			//return base.ConvertFrom(context, culture, value);
		}
		public override Object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, Object value, System.Type destinationType)
		{
			Debug.Assert(destinationType == typeof(String));
			return value.ToString();
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	[TypeConverter(typeof(BfsTypeConverter))]
	public sealed class BootstrapFsPath : ial_base<String>, IEquatable<BootstrapFsPath>
	{
		public static readonly BootstrapFsPath Empty;

		static BootstrapFsPath()
		{
			Empty = new BootstrapFsPath(SysObj.Instance, default(IEnumerable<String>));
		}

		public BootstrapFsPath(IIdentity prv, IEnumerable<String> rgs)
			: base(prv)
		{
			int c, h;
			String s;
			this.arr = rgs as String[];
			if (arr == null)
				arr = rgs == null ? Collection<String>.None : rgs.ToArray();
			if ((h = c = arr.Length) > 0)
				while (--c >= 0)
					h ^= (arr[c] = ((s = arr[c]) != null ? s : "?").ToLower().Trim()).GetHashCode() + c;
			this.hc = h;
		}

		public BootstrapFsPath(IIdentity prv, String s)
			: this(prv, s.Trim(Charset.outer_trim).Split(Charset.dot_sp, StringSplitOptions.RemoveEmptyEntries))
		{
		}

		public readonly String[] arr;
		readonly int hc;

		public FsPath Compile(FeatMgr ftm)
		{
			return arr.Length == 0 ? ftm.im.FspEmpty : new FsPath(ftm.im, this);
		}

		public bool IsEmpty { [DebuggerStepThrough] get { return arr == null || arr.Length == 0; } }

		public override int Count { [DebuggerStepThrough] get { return arr == null ? 0 : arr.Length; } }

		public override String this[int index] { [DebuggerStepThrough] get { return arr[index]; } set { throw not.valid; } }

		public override int IndexOf(String item) { return arr == null ? -1 : Array.IndexOf<String>(arr, item); }

		public override IEnumerator<String> GetEnumerator() { return arr.Enumerator(); }

		public bool GetSlot(ITfsSlot ts_in, out TfsSlot np)
		{
			np = new TfsSlot(ts_in);
			return GetSlot(np, out np);
		}

		public unsafe bool GetSlot(TfsSlot ts_in, out TfsSlot np)
		{
			if (arr == null || arr.Length == 0)
			{
				np = ts_in;
				return true;
			}

			int m;
			if ((m = ts_in.out_mark) != 0)
			{
				var _fmp = ts_in.tfs.ftm.feat_map;
				FeatureInfo fi;
				Edge e;
				int i = 0;
				while (_fmp.TryGetValue(arr[i], out fi) && fi != null && fi.i_feat >= 0)
				{
					int ix1 = ts_in.tfs.GetEdgeIndex(fi.i_feat, m, &e);
					if (ix1 <= 0)
						break;
					if (++i == arr.Length)
					{
						np = new TfsSlot(ts_in.tfs, ix1);
						return true;
					}
					if ((m = e.Mark) == 0)
						break;
				}
			}
			np = default(TfsSlot);
			return false;
		}

		public bool StartsWith(BootstrapFsPath b)
		{
			if (arr == b.arr)
				return true;
			if (arr == null || b.arr == null)
				return false;
			int n = b.arr.Length;
			if (arr.Length < n)
				return false;
			for (int i = 0; i < n; i++)
				if (arr[i] != b.arr[i])
					return false;
			return true;
		}

		public static BootstrapFsPath operator +(BootstrapFsPath a, BootstrapFsPath b)
		{
			String[] a_arr, b_arr;
			if (a == null || (a_arr = a.arr).Length == 0)
				a_arr = null;
			if (b == null || (b_arr = b.arr).Length == 0)
				b_arr = null;

			if (a_arr == null)
				return b_arr == null ? Empty : b;
			if (b_arr == null)
				return a;

			String[] ab = new String[a_arr.Length + b_arr.Length];
			a_arr.CopyTo(ab, 0);
			b_arr.CopyTo(ab, a_arr.Length);
			return new BootstrapFsPath(a.Trace ?? b.Trace, ab);
		}

		public bool Equals(BootstrapFsPath other)
		{
			if (arr == other.arr)
				return true;
			if (arr == null || other.arr == null)
				return false;
			for (int i = 0; i < arr.Length; i++)
				if (!arr[i].Equals(other.arr[i], StringComparison.OrdinalIgnoreCase))
					return false;
			return true;
		}

		public override bool Equals(Object obj) { return obj is BootstrapFsPath && Equals((BootstrapFsPath)obj); }

		public override int GetHashCode() { return hc; }

		public override String ToString() { return arr == null ? "" : arr.StringJoin(".").ToUpper(); }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class FsPath : ial_base<int>, IEquatable<FsPath>
	{
		public static readonly FsPath Empty;
		static FsPath() { Empty = new FsPath(); }
		FsPath() : base(SysObj.Instance) { this.rg_fix = Collection<int>.None; }

		FsPath(Isometrics im, int[] rg_fix, int hc)
			: base(im)
		{
			this.im = im;
			this.rg_fix = rg_fix;
			this.hc = hc;
		}
		FsPath(Isometrics im, int _hc, String[] rgs)
			: this(im, im.ftm.GetFeatureArray(rgs, out _hc), _hc)
		{
		}
		public FsPath(Isometrics im, int[] rg_fix)
			: this(im, rg_fix, 0)
		{
			/// this hash function must match the computation in FeatMgr.GetFeatureArray
			int c = rg_fix.Length;
			this.hc = c;
			for (int i = 0; i < c; i++)
				hc ^= rg_fix[i] << ((i + 6) % 32);
		}
		FsPath(Isometrics im, String[] rgs)
			: this(im, 0, rgs)
		{
		}
		public FsPath(Isometrics im, String path)
			: this(im, path.Trim(Charset.outer_trim).Split(Charset.dot_sp, StringSplitOptions.RemoveEmptyEntries))
		{
		}
		public FsPath(Isometrics im, BootstrapFsPath bfsp)
			: this(im, bfsp == null ? Collection<String>.None : bfsp.arr)
		{
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly Isometrics im;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly int hc;

		[DebuggerDisplay("{FeatureInfoPath}")]
		protected readonly int[] rg_fix;

		public bool IsEmpty { [DebuggerStepThrough] get { return rg_fix.Length == 0; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int FirstIx { [DebuggerStepThrough] get { return IsEmpty ? -1 : rg_fix[0]; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int LastIx { [DebuggerStepThrough] get { return IsEmpty ? -1 : rg_fix[rg_fix.Length - 1]; } }

		public override int this[int index] { [DebuggerStepThrough] get { return rg_fix[index]; } set { throw not.valid; } }

		public override int Count { [DebuggerStepThrough] get { return rg_fix.Length; } }

		public override int IndexOf(int item) { return Array.IndexOf<int>(rg_fix, item); }

		public override IEnumerator<int> GetEnumerator() { return rg_fix.Enumerator(); }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public FeatureInfo[] FeatureInfoPath { [DebuggerStepThrough] get { return rg_fix.Select(i_feat => im.ftm[i_feat]).ToArray(); } }

		/// <summary>
		/// For an FsPath of the form ...FOO.FIRST.REST.REST.REST...., which represents a
		/// feature-structure-style ("cons-cell") list, report the index represented by 
		/// the 'ix_start'th position
		/// </summary>
		public int GetListIndex(int ix_query)
		{
			int ix = ix_query;
			int fix;
			while (ix < rg_fix.Length)
			{
				fix = rg_fix[ix];
				if (fix == im.f_ix_list_head)
					return ix - ix_query;
				if (fix != im.f_ix_list_tail)
					break;
				ix++;
			}
			return -1;
		}

		public FsPath SubPath(int ix_start)
		{
			int[] _new = new int[rg_fix.Length - ix_start];
			for (int i = 0; ix_start < rg_fix.Length; i++, ix_start++)
				_new[i] = rg_fix[ix_start];
			return new FsPath(im, _new);
		}

		/// <summary> ok to use ix1==0 for _top_edge </summary>
		unsafe int Walk(Tfs tfs, int ix1)
		{
			if (tfs == null || ix1 < 0)
				return -1;
			int i, c;
			if ((c = rg_fix.Length) == 0)
				return ix1;

			arr_tfs_entry ate;
			tfs.GetIndexData(ix1, &ate);
			int* pm = &ate.e_Mark;
			i = 0;
			do
				if (*pm == 0 || (ix1 = tfs.GetEdgeIndex(rg_fix[i], *pm, &ate.e)) <= 0)
					return -1;
			while (++i < c);
			return ix1;
		}

		TfsSlot Walk(TfsSlot ts)
		{
			int ix1 = Walk(ts.tfs, ts.ix1);
			return ix1 < 0 ? TfsSlot.NotValid : new TfsSlot(ts.tfs, ix1);
		}

		public TfsSlot this[TfsSlot ts] { get { return Walk(ts); } }

		public TfsSlot this[ITfsSlot ts] { get { return Walk(new TfsSlot(ts.Tfs, ts.SlotIndex)); } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public String TdlString
		{
			[DebuggerStepThrough]
			get
			{
				FeatMgr ftm = im.ftm;
				StringBuilder sb = new StringBuilder(80);
				for (int i = 0; i < rg_fix.Length; i++)
				{
					if (sb.Length > 0)
						sb.Append('.');
					int fix = rg_fix[i];
					if (fix < 0 || fix >= ftm.Count)
						throw new Exception();
					sb.Append(ftm[fix].FEATURE);
				}
				return sb.ToString();
			}
		}

		String _render_to_string()
		{
			if (im == null)
				return "(empty path)";
			FeatMgr ftm = im.ftm;
			StringBuilder sb = new StringBuilder(80);
			for (int i = 0; i < rg_fix.Length; i++)
			{
				if (sb.Length > 0)
					sb.Append('.');
				int fix = rg_fix[i];
				if (fix < 0 || fix >= ftm.Count)
					sb.AppendFormat("[???({0})]", fix.ToString());
				else
					sb.Append(ftm[fix].FEATURE);
			}
			return sb.ToString();
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		String _s;
		public override String ToString()
		{
			if (_s == null)
				_s = _render_to_string();
			return _s;
		}

		public static FsPath operator +(FsPath fsp, int ifeat)
		{
			var im = fsp.im;
			if (ifeat == -1 || ifeat >= im.ftm.Count)
				throw new Exception();
			return new FsPath(im, alib.Array.arr.Append(fsp.rg_fix, ifeat));
		}

		public static FsPath operator +(FsPath fsp1, FsPath fsp2)
		{
			Isometrics im;
			if ((im = fsp1.im) != fsp2.im)
				throw new Exception();
			int c1 = fsp1.rg_fix.Length, c2 = fsp2.rg_fix.Length;
			if (c1 == 0)
				return fsp2;
			if (c2 == 0)
				return fsp1;
			var rgfix = new int[c1 + c2];
			fsp1.rg_fix.CopyTo(rgfix, 0);
			fsp2.rg_fix.CopyTo(rgfix, c1);
			return new FsPath(im, rgfix);
		}

		public bool Equals(FsPath other)
		{
			if ((Object)other == default(Object) || hc != other.hc)
				return false;
			for (int i = 0; i < rg_fix.Length; i++)
				if (rg_fix[i] != other.rg_fix[i])
					return false;
			return true;
		}
		public override bool Equals(Object obj)
		{
			throw new NotImplementedException("Please use strongly-typed override.");
		}

		public override int GetHashCode() { return hc; }
	};
}
